(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' 
    ? factory(module) 
    : typeof define === 'function' && define.amd 
      ? define(['exports', function (exports) {
        var module = { exports: {} };
        factory(module);
        exports = module.exports;
      }])
      : (function () {
        var module = { exports: {} };
        factory(module);
        global.Event = module.exports;
      }())
})(this, function (module) {

  /**
   * 判断某个函数是否由浏览器自身实现
   */
  function isNative (Ctor) {
    return typeof Ctor === 'function' && /native code/.test(Ctor.toString());
  }

  // 延迟回调触发器
  var delay;
  {
    if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
      delay = setImmediate;
    } else if (isNative(typeof Promise !== 'undefined' && Promise)) {
      delay = function (fn) {
        Promise.resolve()
          .then(fn)
      }
    } else {
      delay = function (fn) {
        setTimeout(fn, 0);
      }
    }
  }

  /**
   * 回调函数处理器,用于统一话调用回调
   */
  function fnHandle (fns) {
    if (!Array.isArray(fns)) {
      fns = [fns];
    }

    var handle = function handle() {
      for (var i = 0; i < fns.length; i++) {
        fns[i].apply(this, arguments);
      }
    }
    handle.fns = fns;

    return handle
  }
  
  /* ------------- */

  function Event () {
    // 事件回调存储栈
    this.stacks = {}
  }

  /**
   * 绑定事件统一处理器
   */
  Event.prototype.__bind = function __bind (name, fns) {
    var fn = fnHandle(fns);

    if (!this.stacks[name]) {
      this.stacks[name] = fnHandle(function() { })
    }

    this.stacks[name].fns.push(fn);

    return fn;
  }

  /**
   * 绑定事件及回调函数
   */
  Event.prototype.on = function on (name, fns) {
    this.__bind(name, fns);
  }

  /**
   * 单次绑定,一次触发就失效
   */
  Event.prototype.once = function once (name, fns) {
    var fn = this.__bind(name, fns);
    fn.isOnce = true;
  }

  /**
   * 发布事件,及如果事一次绑定的移除引用调用
   */
  Event.prototype.emit = function emit (name) {
    var callback = this.stacks[name];
    var args = Array.prototype.slice.call(arguments, 1);

    if (callback) {
      delay(function () {
        callback.apply(this, args);
        var fns = callback.fns,
            i = 0;
        while (fns[i]) {
          if (fns[i].isOnce) {
            fns.splice(i, 1);
          } else {
            i++
          }
        }
      })
    }
  }
  
  module.exports = Event;
});

